<?php

declare(strict_types=1);

namespace App\Service\Client;


use App\Amqp\DelayProducer;
use App\Amqp\Producer\NoticeTaskAuditPendingProducer;
use App\Amqp\Producer\TaskReturnProducer;
use App\Constants\DefaultValue;
use App\Constants\ErrorCode;
use App\Constants\Model\EmployerBalance as EmployerBalanceConstants;
use App\Constants\RedisKey;
use App\Dao\AdminUserDao;
use App\Dao\ConfigOtherDao;
use App\Dao\CooperativeDao;
use App\Dao\CooperativeUserDao;
use App\Dao\NoticeAdminDao;
use App\Dao\NoticeCooperativeDao;
use App\Dao\NoticeEmployerDao;
use App\Dao\TaskDao;
use App\Dao\TaskExecDao;
use App\Dao\TaskStepDao;
use App\Dao\TaskTagDao;
use App\Dao\UserDao;
use App\Dao\UserEmployerDao;
use App\Exception\BusinessException;
use App\Model\EmployerBalance;
use App\Model\Task;
use App\Model\TaskExec;
use App\Model\TaskStep;
use App\Model\UserEmployer;
use App\Service\Common\TaskLogic;
use App\Utils\Lock;
use App\Utils\Log;
use App\Utils\Notice\AdminTaskAuditPending;
use App\Utils\Notice\CooperativeTaskAuditPending;
use App\Utils\Notice\CooperativeTaskAuditWait;
use App\Utils\Notice\EmployerTaskAuditWait;
use App\Utils\Notice\EmployerTaskRefund;
use App\Utils\Notice\Sender;
use App\Utils\Notice\UserTaskAuditFail;
use App\Utils\Notice\UserTaskAuditSuccess;
use Hyperf\Amqp\Result;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use App\Constants\Model\Task as TaskConstants;
use App\Constants\Model\TaskExec as TaskExecConstants;
use App\Constants\Model\Notice as NoticeConstants;
use Hyperf\HttpServer\Request;


class TaskEmployerService extends BaseService
{
    /**
     * @Inject()
     * @var DelayProducer
     */
    protected $delayProducer;

    /**
     * @Inject()
     * @var TaskLogic
     */
    protected $taskLogic;


    /**
     * @Inject()
     * @var UserDao
     */
    protected $userDao;

    /**
     * @Inject()
     * @var UserEmployerDao
     */
    protected $userEmployerDao;


    /**
     * @Inject()
     * @var TaskDao
     */
    protected $taskDao;

    /**
     * @Inject()
     * @var TaskExecDao
     */
    protected $taskExecDao;

    /**
     * @Inject()
     * @var TaskStepDao
     */
    protected $taskStepDao;

    /**
     * @Inject()
     * @var ConfigOtherDao
     */
    protected $configOtherDao;

    /**
     * @Inject()
     * @var CooperativeUserDao
     */
    protected $cooperativeUserDao;

    /**
     * @Inject()
     * @var CooperativeDao
     */
    protected $cooperativeDao;

    /**
     * @Inject()
     * @var AdminUserDao
     */
    protected $adminUserDao;

    /**
     * @Inject()
     * @var NoticeEmployerDao
     */
    protected $noticeEmployerDao;

    /**
     * @Inject()
     * @var NoticeCooperativeDao
     */
    protected $noticeCooperativeDao;

    /**
     * @Inject()
     * @var NoticeAdminDao
     */
    protected $noticeAdminDao;

    /**
     * @Inject()
     * @var TaskTagDao
     */
    protected $taskTagDao;


    public function statistics(): array
    {
        $request = di(Request::class);
        $cooperative_id = $request->getHeaderLine('cooperative_id');

        $userIds = [$this->userInfo()->id];

        if ($cooperative_id && $cooperative_id != '') {
            if ($cooperative_id == '-1') {
                $whereUser = [
                    'phone' => $this->userInfo()->phone,
                ];
            } else {
                $whereUser = [
                    'cooperative_id' => $cooperative_id,
                    'phone' => $this->userInfo()->phone
                ];
            }
            $users = $this->userDao->select($whereUser);
            if (!$users) {
                throw new BusinessException(ErrorCode::USER_NOT_EXIST);
            }

            $usersArray = $users->toArray();
            $userIds = array_column($usersArray, 'id');
        }

        $taskPrelude = 0;
        $taskShow = 0;
        $taskPause = 0;
        $taskFail = 0;
        $processCount = 0;
        $processPrelude = 0;
        $processArbitration = 0;

        foreach ($userIds as $v) {
            $where = [
                'employer_id' => $v,
            ];

            //待审核
            $taskPrelude += Task::query()
                ->where($where)
                ->where('audit_status', TaskConstants::AUDIT_STATUS_PENDING)
                ->where('disable_status', TaskConstants::DISABLE_STATUS_NO)
                ->count();
            //正在展示
            $taskShow += Task::query()
                ->where($where)
                ->where('audit_status', TaskConstants::AUDIT_STATUS_ALLOW)
                ->where('disable_status', TaskConstants::DISABLE_STATUS_NO)
                ->where('show_status', TaskConstants::SHOW_STATUS_ONLINE)
                ->count();
            //已暂停
            $taskPause += Task::query()
                ->where($where)
                ->where(
                    function ($query) {
                        $query->where('disable_status', TaskConstants::DISABLE_STATUS_NO)
                            ->where('audit_status', TaskConstants::AUDIT_STATUS_ALLOW)
                            ->whereIn(
                                'show_status',
                                [
                                    TaskConstants::SHOW_STATUS_OFFLINE,
                                    TaskConstants::SHOW_STATUS_END,
                                    TaskConstants::SHOW_STATUS_REFUNDED
                                ]
                            );
                    }
                )
                ->orWhere(
                    function ($query) {
                        $query->where('disable_status', TaskConstants::DISABLE_STATUS_YES);
                    }
                )
                ->count();
            //审核不通过
            $taskFail += Task::query()
                ->where($where)
                ->where('audit_status', TaskConstants::AUDIT_STATUS_NOT_ALLOW)
                ->where('disable_status', TaskConstants::DISABLE_STATUS_NO)
                ->count();


            //已被领取总数
            $processCount += TaskExec::query()
                ->where($where)
                ->count();
            //待审核
            $processPrelude += TaskExec::query()
                ->where($where)
                ->where('status', TaskExecConstants::STATUS_HAS_AUDIT)
                ->where('audit_status', TaskExecConstants::AUDIT_STATUS_NOT)
                ->count();
            //仲裁
            $processArbitration += TaskExec::query()
                ->where($where)
                ->where('status', TaskExecConstants::STATUS_ARBITRATION)
                ->count();
        }

        return [
            'taskPrelude' => $taskPrelude,
            'taskShow' => $taskShow,
            'taskPause' => $taskPause,
            'taskFail' => $taskFail,
            'processCount' => $processCount,
            'processPrelude' => $processPrelude,
            'processArbitration' => $processArbitration
        ];
    }


    /**
     * 任务发布
     * @param array $data
     * @return array
     */
    public function publish(array $data): array
    {
        $lockKey = RedisKey::LOCK_MYSQL_TABLE_ID . 'user_employer:' . $this->userInfo()->employer->id;
        $Lock = new Lock();
        $lockState = $Lock->lock($lockKey, DefaultValue::LOCK_TTL);
        if (!$lockState) {
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        /** @var UserEmployer $userEmployer */
        $userEmployer = $this->userEmployerDao->first(['id' => $this->userInfo()->employer->id]);


        $commissionPercent = $this->configOtherDao->getCommissionPercent($this->userInfo()->cooperative_id);
        //佣金+平台抽成
//        $totalPrice = $data['price'] * $data['count'];
//        $totalPrice = $totalPrice + $totalPrice * $commissionPercent;
        $totalPrice = bcmul(
            bcmul((string)$data['price'], (string)$data['count'], 2),
            bcadd((string)1, (string)$commissionPercent, 2),
            2
        );


        if ((float)$totalPrice > (float)$userEmployer->balance) {
            throw new BusinessException(ErrorCode::EMPLOYER_BALANCE_NOT_ENOUGH);
        }

        $filePathCoverImage = $data['cover_image'];
//        $filePathCoverImage = $this->moveTmpFile($data['cover_image'], '/upload/task');

        //步骤图片路径
        $filePathStepImage1 = [];
        $filePathStepImage2 = [];
        $filePathStepImage3 = [];

        try {
            $filePathStepImage1 = $this->uploadStepImage($data, 'step_image_1', 3);
            $filePathStepImage2 = $this->uploadStepImage($data, 'step_image_2', 5);
            $filePathStepImage3 = $this->uploadStepImage($data, 'step_image_3', 3);
        } catch (\Throwable $e) {
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());
            throw new BusinessException($e->getCode());
        }

        $autoPayment = $this->configOtherDao->isAutoPayment($this->userInfo()->cooperative_id);

        Db::beginTransaction();
        try {
            $userEmployer->balance = $userEmployer->balance - $totalPrice;
            $userEmployer->save();

            $task = new Task();
            $task->fill($data);
            $task->cover_image = $filePathCoverImage;
            $tags = explode(',', $data['tags']);
            $tags = $this->taskTagDao->whereIn(['id' => $tags], ['tag', 'color']);
            if (!$tags) {
                throw new BusinessException(ErrorCode::COOPERATIVE_TAG_NOT_FOUND);
            }
            $task->tags = json_encode($tags, JSON_UNESCAPED_UNICODE);
            $task->audit_status = TaskConstants::AUDIT_STATUS_PENDING;
            $task->total_price = $totalPrice;
            $task->remaining_count = $data['count'];
            $task->end_time = strtotime("{$data['end_time']} 23:59:59");
            $task->commission_percent = $commissionPercent;
            $task->disable_status = TaskConstants::DISABLE_STATUS_NO;
            $task->cooperative_id = $this->userInfo()->cooperative_id;
            $task->employer_id = $this->userInfo()->id;
            $task->auto_payment = $autoPayment;
            //任务审核归属
            if ($this->cooperativeDao->isCanAuditTask($this->userInfo()->cooperative_id)) {
                $noticeSendPlatform = NoticeConstants::SEND_PLATFORM_COOPERATIVE;
                $task->is_audit_by = TaskConstants::IS_AUDIT_BY_COOPERATIVE;
            } else {
                $noticeSendPlatform = NoticeConstants::SEND_PLATFORM_ADMIN;
                $task->is_audit_by = TaskConstants::IS_AUDIT_BY_ADMIN;
            }
            $task->save();

            //悬赏主账户流水
            $employerBalance = new EmployerBalance();
            $employerBalance->user_id = $this->userInfo()->id;
            $employerBalance->cooperative_id = $this->userInfo()->cooperative_id;
            $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_TASK_PUBLISH;
            $employerBalance->money = $totalPrice;
            $employerBalance->detail = EmployerBalanceConstants::ACCOUNT_TYPE_TEXT[EmployerBalanceConstants::ACCOUNT_TYPE_TASK_PUBLISH];
            $employerBalance->save();

            //待审核通知
            $message = new NoticeTaskAuditPendingProducer(
                [
                    'noticeSendPlatform' => $noticeSendPlatform,
                    'cooperative_id' => $this->userInfo()->cooperative_id
                ]
            );
            $this->delayProducer->produce($message, 1);

            if (isset($data['step_text_10']) || isset($data['step_image_10'])) {
                $taskStep10 = new TaskStep();
                $taskStep10->task_id = $task->id;
                $taskStep10->step_type = 10;
                $taskStep10->text = isset($data['step_text_10']) ? $data['step_text_10'] : '';
                $images = implode('|', array_filter($filePathStepImage1));
                $imagesString = '';
                if ($images) {
                    $imagesString = $images . '|';
                }
                $taskStep10->image = $imagesString;
                $taskStep10->save();
            }

            for ($i = 0; $i < 5; $i++) {
                $stepTextKey = "step_text_2{$i}";
                $stepImageKey = "step_image_2{$i}";
                if (isset($data[$stepTextKey]) || isset($data[$stepImageKey])) {
                    $taskStep2[$i] = new TaskStep();
                    $taskStep2[$i]->task_id = $task->id;
                    $taskStep2[$i]->step_type = "2{$i}";
                    $taskStep2[$i]->text = isset($data[$stepTextKey]) ? $data[$stepTextKey] : '';
                    $taskStep2[$i]->image = $filePathStepImage2[$i];
                    $taskStep2[$i]->save();
                }
            }

            for ($i = 0; $i < 3; $i++) {
                $stepTextKey = "step_text_3{$i}";
                $stepImageKey = "step_image_3{$i}";
                if (isset($data[$stepTextKey]) || isset($data[$stepImageKey])) {
                    $taskStep3[$i] = new TaskStep();
                    $taskStep3[$i]->task_id = $task->id;
                    $taskStep3[$i]->step_type = "3{$i}";
                    $taskStep3[$i]->text = isset($data[$stepTextKey]) ? $data[$stepTextKey] : '';
                    $taskStep3[$i]->image = $filePathStepImage3[$i];
                    $taskStep3[$i]->save();
                }
            }

            Db::commit();
            $Lock->unlock($lockKey);

            return ['task_id' => $task->id];
        } catch (\Throwable $e) {
            Db::rollBack();
            $Lock->unlock($lockKey);
            Log::get()->error($e->getCode(), [$e->getMessage(), $data]);
            Log::get()->error($e->getTraceAsString());
            var_dump($e->getMessage());
            var_dump($e->getTraceAsString());
            throw new BusinessException(ErrorCode::TASK_PUBLISH_ERROR);
        }
    }

    /**
     * 任务列表
     * @param array $data
     * @return array
     */
    public function list(array $data): array
    {
        $perPage = isset($data['perPage']) ? (int)$data['perPage'] : DefaultValue::PER_PAGE_DEFAULT;

        $employerIds = $this->userIdsInCooperatives();
        $where = [
            'employer_ids' => $employerIds,
        ];

        //status：1待审核，2审核失败，3展示中，4暂停/结束，5已被领取
        switch ((int)$data['status']) {
            case TaskConstants::TASK_LIST_STATUS_NOT_AUDIT:
                $where['audit_status'] = TaskConstants::AUDIT_STATUS_PENDING;
                break;
            case TaskConstants::TASK_LIST_STATUS_AUDIT_FAIL:
                $where['audit_status'] = TaskConstants::AUDIT_STATUS_NOT_ALLOW;
                break;
            case TaskConstants::TASK_LIST_STATUS_SHOW:
                $where['audit_status'] = TaskConstants::AUDIT_STATUS_ALLOW;
                $where['show_status'] = TaskConstants::SHOW_STATUS_ONLINE;
                $where['disable_status'] = TaskConstants::DISABLE_STATUS_NO;
                break;
        }


        /** @var \Hyperf\Contract\LengthAwarePaginatorInterface $page */
        $page = [];
        switch ((int)$data['status']) {
            case TaskConstants::TASK_LIST_STATUS_NOT_AUDIT:
            case TaskConstants::TASK_LIST_STATUS_AUDIT_FAIL:
            case TaskConstants::TASK_LIST_STATUS_SHOW:
                $page = $this->taskDao->paginate(
                    $where,
                    $perPage,
                    ['*'],
                    ['orderBy' => ['created_at', 'desc']]
                );
                break;
            case TaskConstants::TASK_LIST_STATUS_PAUSE_OR_END:
                $page = $this->taskDao
                    ->where($where)
                    ->where('disable_status', TaskConstants::FINISH_STATUS_PART)
                    ->orWhere(
                        function ($query) {
                            $query->where('employer_id', $this->userInfo()->id)
                                ->where('audit_status', TaskConstants::AUDIT_STATUS_ALLOW)
                                ->whereIn(
                                    'show_status',
                                    [
                                        TaskConstants::SHOW_STATUS_OFFLINE,
                                        TaskConstants::SHOW_STATUS_END,
                                        TaskConstants::SHOW_STATUS_REFUNDED
                                    ]
                                );
                        }
                    )
                    ->paginate($perPage);
                break;
            case TaskConstants::TASK_LIST_STATUS_HAS_RECEIVE:
                $page = $this->taskDao
                    ->where($where)
                    ->with(
                        [
                            'taskExec' => function ($query) {
                                $query->limit(10);
                            }
                        ]
                    )
                    ->with('taskExec.user')
//                    ->where('finish_status', TaskConstants::FINISH_STATUS_PART)
                    ->whereRaw('count > remaining_count')
                    ->paginate($perPage);
        }

        $result = [];
        switch ((int)$data['status']) {
            case TaskConstants::TASK_LIST_STATUS_NOT_AUDIT:
            case TaskConstants::TASK_LIST_STATUS_AUDIT_FAIL:
            case TaskConstants::TASK_LIST_STATUS_SHOW:
            case TaskConstants::TASK_LIST_STATUS_PAUSE_OR_END:
                /** @var Task $v */
                foreach ($page->items() as $v) {
                    $v['gold'] = $this->price2gold($v->price);

                    $endTimeDay = 0;
                    $diffTime = $v->end_time - time();
                    if ($diffTime > 0) {
                        $endTimeDay = ceil($diffTime / 86400);
                    }

                    //剩余天数
                    $v->end_time_day = $endTimeDay;
                    $v->receiverCount = bcsub(
                        (string)$v->count,
                        (string)$v->remaining_count
                    );
                    $result[] = $v;
                }
                break;
            case TaskConstants::TASK_LIST_STATUS_HAS_RECEIVE:
                /** @var Task $v */
                foreach ($page->items() as $v) {
                    //剩余天数
                    $endTimeDay = 0;
                    $diffTime = $v->end_time - time();
                    if ($diffTime > 0) {
                        $endTimeDay = ceil($diffTime / 86400);
                    }
                    $v->end_time_day = $endTimeDay;


                    //用户信息
                    $taskExecArray = $v->taskExec->toArray();
                    $users = array_column($taskExecArray, 'user');
                    $usersInfo = [];
                    if (count($users) > 0) {
                        foreach ($users as $v2) {
                            $usersInfo[] = [
                                'id' => $v2['id'],
                                'avatar' => $v2['avatar']
                            ];;
                        }
                    }
                    $v->receiverCount = bcsub(
                        (string)$v->count,
                        (string)$v->remaining_count
                    );
                    $v->users = $usersInfo;
                    unset($v->taskExec);

                    $result[] = $v;
                }
                break;
        }


        return [
            'currentPage' => $page->currentPage(),
            'total' => $page->total(),//数据总数
            'hasMorePages' => $page->hasMorePages(),
            'data' => $result
        ];
    }


    /**
     * 任务修改
     * @param $id
     * @param array $data
     * @return array
     */
    public function update($id, array $data): array
    {
        $Lock = new Lock();

        $lockKeyEmployer = RedisKey::LOCK_MYSQL_TABLE_ID . 'user_employer:' . $this->userInfo()->employer->id;
        $lockStateEmployer = $Lock->lock($lockKeyEmployer, DefaultValue::LOCK_TTL);
        if (!$lockStateEmployer) {
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        $lockKeyTask = RedisKey::LOCK_MYSQL_TABLE_ID . 'task:' . $id;
        $lockStateTask = $Lock->lock($lockKeyTask, DefaultValue::LOCK_TTL);
        if (!$lockStateTask) {
            $Lock->unlock($lockKeyEmployer);
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        /** @var Task $taskOld */
        $taskOld = $this->taskDao->first(['id' => $id, 'employer_id' => $this->userInfo()->id]);
        if (!$taskOld) {
            $Lock->unlock($lockKeyEmployer);
            $Lock->unlock($lockKeyTask);
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }


        $taskStepOld = $this->taskStepDao->first(['task_id' => $taskOld->id]);
        if (!$taskStepOld) {
            $taskStepOld = [];
        }

        //旧任务图片
        $filePathStepImage1Old = [];
        $filePathStepImage2Old = [];
        $filePathStepImage3Old = [];
        foreach ($taskStepOld as $v) {
            switch ((int)$v['step_type']) {
                case 10:
                    $filePathStepImage1Old = array_filter(explode('|', $v['image']));
                    break;
                case 20:
                case 21:
                case 22:
                case 23:
                case 24:
                    if ($v['image'] != '') {
                        $filePathStepImage2Old[] = $v['image'];
                    }
                    break;
                case 30:
                case 31:
                case 32:
                    if ($v['image'] != '') {
                        $filePathStepImage3Old[] = $v['image'];
                    }
                    break;
            }
        }

        /** @var UserEmployer $userEmployer */
        $userEmployer = $this->userEmployerDao->first(['id' => $this->userInfo()->employer->id]);


        $commissionPercent = $this->configOtherDao->getCommissionPercent($this->userInfo()->cooperative_id);
        //佣金+平台抽成
//        $totalPrice = $data['price'] * $data['count'];
//        $totalPrice = $totalPrice + $totalPrice * $commissionPercent;
        $totalPrice = bcmul(
            bcmul((string)$data['price'], (string)$data['count'], 2),
            bcadd((string)1, (string)$commissionPercent, 2),
            2
        );

//        $oldPrice = $taskOld->price * $taskOld->count;
        $oldPrice = bcmul((string)$taskOld->price, (string)$taskOld->count, 2);

        if ($totalPrice > ($userEmployer->balance + $oldPrice)) {
            throw new BusinessException(ErrorCode::EMPLOYER_BALANCE_NOT_ENOUGH);
        }

        //主图
        $filePathCoverImage = $data['cover_image'];

        $filePathStepImage1 = $this->uploadStepImage($data, 'step_image_1', 3);
        $filePathStepImage2 = $this->uploadStepImage($data, 'step_image_2', 5);
        $filePathStepImage3 = $this->uploadStepImage($data, 'step_image_3', 3);

        Db::beginTransaction();
        try {
            $userEmployer->balance = $userEmployer->balance + $oldPrice - $totalPrice;
            $userEmployer->save();

            //悬赏主账户流水
            $diffPrice = bcsub((string)$oldPrice, (string)$totalPrice, 2);
            if ($diffPrice != 0) {
                $employerBalance = new EmployerBalance();
                $employerBalance->user_id = $this->userInfo()->id;
                $employerBalance->cooperative_id = $this->userInfo()->cooperative_id;
                if ($diffPrice > 0) {
                    //返款
                    $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_TASK_UPDATE_ADD;
                    $employerBalance->detail = EmployerBalanceConstants::ACCOUNT_TYPE_TEXT[EmployerBalanceConstants::ACCOUNT_TYPE_TASK_UPDATE_ADD];
                } elseif ($diffPrice < 0) {
                    //扣款
                    $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_TASK_UPDATE_SUB;
                    $employerBalance->detail = EmployerBalanceConstants::ACCOUNT_TYPE_TEXT[EmployerBalanceConstants::ACCOUNT_TYPE_TASK_UPDATE_SUB];
                }
                $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_RECHARGE;
                $employerBalance->money = $diffPrice;
                $employerBalance->save();
            }


            $task = new Task();
            $task->fill($data);
            $task->cover_image = $filePathCoverImage;
            $tags = explode(',', $data['tags']);
            $tags = $this->taskTagDao->whereIn(['id' => $tags], ['tag', 'color']);
            if (!$tags) {
                throw new BusinessException(ErrorCode::COOPERATIVE_TAG_NOT_FOUND);
            }
            $task->tags = json_encode($tags, JSON_UNESCAPED_UNICODE);
            $task->audit_status = TaskConstants::AUDIT_STATUS_PENDING;
            $task->total_price = $totalPrice;
            $task->remaining_count = $data['count'];
            $task->commission_percent = $commissionPercent;
            $task->disable_status = TaskConstants::DISABLE_STATUS_NO;
            $task->cooperative_id = $this->userInfo()->cooperative_id;
            $task->employer_id = $this->userInfo()->id;
            $task->auto_payment = $this->configOtherDao->isAutoPayment($this->userInfo()->cooperative_id);
            $task->save();


            if (isset($data['step_text_10']) || isset($data['step_image_10'])) {
                $taskStep10 = new TaskStep();
                $taskStep10->task_id = $task->id;
                $taskStep10->step_type = 10;
                $taskStep10->text = isset($data['step_text_10']) ? $data['step_text_10'] : '';
                $taskStep10->image = $filePathStepImage1 ? implode('|', array_filter($filePathStepImage1)) : '';
                $taskStep10->save();
            }

            for ($i = 0; $i < 5; $i++) {
                $stepTextKey = "step_text_2{$i}";
                $stepImageKey = "step_image_2{$i}";
                if (isset($data[$stepTextKey]) || isset($data[$stepImageKey])) {
                    $taskStep2[$i] = new TaskStep();
                    $taskStep2[$i]->task_id = $task->id;
                    $taskStep2[$i]->step_type = "2{$i}";
                    $taskStep2[$i]->text = isset($data[$stepTextKey]) ? $data[$stepTextKey] : '';
                    $taskStep2[$i]->image = $filePathStepImage2[$i];
                    $taskStep2[$i]->save();
                }
            }

            for ($i = 0; $i < 3; $i++) {
                $stepTextKey = "step_text_3{$i}";
                $stepImageKey = "step_image_3{$i}";
                if (isset($data[$stepTextKey]) || isset($data[$stepImageKey])) {
                    $taskStep3[$i] = new TaskStep();
                    $taskStep3[$i]->task_id = $task->id;
                    $taskStep3[$i]->step_type = "3{$i}";
                    $taskStep3[$i]->text = isset($data[$stepTextKey]) ? $data[$stepTextKey] : '';
                    $taskStep3[$i]->image = $filePathStepImage3[$i];
                    $taskStep3[$i]->save();
                }
            }

            //删除旧数据
            Task::query()->where(['id' => $taskOld->id])->delete();
            TaskStep::query()->where(['task_id' => $taskOld->id])->delete();

            Db::commit();

            return ['task_id' => $task->id];
        } catch (\Throwable $e) {
            Db::rollBack();
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());
            throw new BusinessException(ErrorCode::TASK_UPDATE_ERROR);
        } finally {
            $Lock->unlock($lockKeyEmployer);
            $Lock->unlock($lockKeyTask);
        }
    }

    /**
     * 任务修改-上/下架
     * @param $id
     * @param array $data
     */
    public function updateShowStatus($id, array $data): void
    {
        /** @var Task $task */
        $task = $this->taskDao->first(['id' => $id, 'employer_id' => $this->userInfo()->id]);
        if (!$task) {
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }
        $task->show_status = $data['status'];
        $task->save();
    }

    /**
     * 任务退款
     * @param $id
     */
    public function refund($id): void
    {
        $Lock = new Lock();

        $lockKeyTask = RedisKey::LOCK_MYSQL_TABLE_ID . 'task:' . $id;
        $lockStateTask = $Lock->lock($lockKeyTask, DefaultValue::LOCK_TTL);
        if (!$lockStateTask) {
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        /** @var Task $task */
        $task = $this->taskDao->first(['id' => $id, 'employer_id' => $this->userInfo()->id]);
        if (!$task) {
            $Lock->unlock($lockKeyTask);
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }

        //防止重复退款
        if ($task->show_status == TaskConstants::SHOW_STATUS_REFUNDED) {
            $Lock->unlock($lockKeyTask);
            throw new BusinessException(ErrorCode::TASK_REFUND_REPEAT);
        }


        $lockKeyUserEmployer = RedisKey::LOCK_MYSQL_TABLE_ID . 'user:' . $this->userInfo()->id;
        $lockStateUserEmployer = $Lock->lock($lockKeyUserEmployer, DefaultValue::LOCK_TTL);
        if (!$lockStateUserEmployer) {
            $Lock->unlock($lockKeyTask);
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }


        Db::beginTransaction();
        try {
            //返款 = 单价x剩余量x(1+抽成比例)
            $refundPrice = bcmul(
                bcmul((string)$task->price, (string)$task->remaining_count, 2),
                bcadd((string)1, (string)$task->commission_percent, 2),
                2
            );

            if ($refundPrice > 0) {
                /** @var UserEmployer $userEmployer */
                $userEmployer = $this->userEmployerDao->first(['user_id' => $this->userInfo()->id]);
                $userEmployer->balance = bcadd((string)$userEmployer->balance, (string)$refundPrice, 2);
                $userEmployer->save();

                //悬赏主账户流水
                $employerBalance = new EmployerBalance();
                $employerBalance->user_id = $this->userInfo()->id;
                $employerBalance->cooperative_id = $this->userInfo()->cooperative_id;
                $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_TASK_RETURN;
                $employerBalance->money = $refundPrice;
                $employerBalance->detail = EmployerBalanceConstants::ACCOUNT_TYPE_TEXT[EmployerBalanceConstants::ACCOUNT_TYPE_TASK_RETURN];
                $employerBalance->save();
            }
            $task->finish_status = TaskConstants::FINISH_STATUS_PART;
            $task->disable_status = TaskConstants::DISABLE_STATUS_NO;
            $task->show_status = TaskConstants::SHOW_STATUS_REFUNDED;
            $task->save();


            //锁定金额= 单价x（总数-剩余量-已完成）x（1+抽成比例）
            $lockNumber = $task->count - $task->done_count - $task->remaining_count;
            $lockPrice = bcmul(
                bcmul((string)$task->price, (string)$lockNumber, 2),
                bcadd((string)1, (string)$task->commission_percent, 2),
                2
            );

            //任务退款通知
            Sender::send(
                [$task->employer_id],
                new EmployerTaskRefund(
                    [
                        'replaces' => [
                            'refundPrice' => $refundPrice,
                            'lockPrice' => $lockPrice
                        ],
                    ]
                )
            );

            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());
            throw new BusinessException(ErrorCode::TASK_REFUND_ERROR);
        } finally {
            $Lock->unlock($lockKeyTask);
            $Lock->unlock($lockKeyUserEmployer);
        }
    }


    /**
     * 被领取任务进度-列表
     * @param array $data
     * @return array
     */
    public function processList(array $data): array
    {

        $perPage = isset($data['perPage']) ? (int)$data['perPage'] : DefaultValue::PER_PAGE_DEFAULT;

        $employerIds = $this->userIdsInCooperatives();
        $where = [
            'employer_ids' => $employerIds,
        ];

        //status：提交任务审核状态：1待审核，2通过，3失败,仲裁状态：4待审核，5已审核
        switch ((int)$data['status']) {
            case TaskExecConstants::EMPLOYER_TASK_LIST_STATUS_NOT_AUDIT:
                $where['status'] = TaskExecConstants::STATUS_HAS_AUDIT;
                $where['audit_status'] = TaskExecConstants::AUDIT_STATUS_NOT;
                break;
            case TaskExecConstants::EMPLOYER_TASK_LIST_STATUS_AUDIT_SUCCESS:
                $where['status'] = TaskExecConstants::STATUS_HAS_AUDIT;
                $where['audit_status'] = TaskExecConstants::AUDIT_STATUS_SUCCESS;
                break;
            case TaskExecConstants::EMPLOYER_TASK_LIST_STATUS_AUDIT_FAIL:
                $where['status'] = TaskExecConstants::STATUS_HAS_AUDIT;
                $where['audit_status'] = TaskExecConstants::AUDIT_STATUS_FAIL;
                break;
            case TaskExecConstants::EMPLOYER_TASK_LIST_STATUS_NOT_ARBITRATE:
                $where['status'] = TaskExecConstants::STATUS_ARBITRATION;
                $where['arbitration_status'] = TaskExecConstants::ARBITRATION_STATUS_NOT;
                break;
            case TaskExecConstants::EMPLOYER_TASK_LIST_STATUS_HAS_ARBITRATE:
                $where['status'] = TaskExecConstants::STATUS_ARBITRATION;
                $where['arbitration_status'] = TaskExecConstants::ARBITRATION_STATUS_HAS;
                break;
        }

        $page = $this->taskExecDao
            ->where($where)
            ->with('task')
            ->with('taskArbitration')
            ->orderBy('updated_at', 'desc')
            ->paginate($perPage);

        $data = [];
        /** @var TaskExec $v */
        foreach ($page->items() as $v) {
            $data[] = [
                'task_id' => $v->task->id,
                'task_exec_id' => $v->id,
                'task_cover_image' => $v->task->cover_image,
                'task_title' => $v->task->title,
                'remaining_time' => $v->remaining_time,
                'remaining_time_surplus' => ($v->remaining_time - time()) > 0 ? ($v->remaining_time - time()) : 0,
                'price' => $v->task->price,
                'gold' => $this->price2gold($v->task->price),
                'audit_status' => $v->audit_status,
                'finish_status' => $v->finish_status,
                'status' => $v->status,
                'audit_time_end' => $v->audit_time_end,
                'audit_time_end_surplus' => ($v->audit_time_end - time()) > 0 ? ($v->audit_time_end - time()) : 0,
                'audit_fail_context' => $v->audit_fail_context,
                'audit_fail_overtime' => $v->audit_fail_time != 0 ? bcadd(
                    (string)$v->audit_fail_time,
                    (string)TaskExecConstants::AUDIT_FAIL_OVERTIME
                ) : 0,
                'audit_fail_surplus' => ($v->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time(
                    )) > 0 ? ($v->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time()) : 0,
                'arbitration_status' => $v->arbitration_status,
                'arbitration' => [
                    'status' => isset($v->taskArbitration->status) ? $v->taskArbitration->status : '',
                    'context' => isset($v->taskArbitration->context) ? $v->taskArbitration->context : ''
                ],
                'user_avatar' => $v->user->avatar,
                'user_nickname' => $v->user->nickname,
            ];
        }

        return [
            'currentPage' => $page->currentPage(),
            'total' => $page->total(),//数据总数
            'hasMorePages' => $page->hasMorePages(),
            'data' => $data
        ];
    }

    /**
     * 任务进度-详情
     * @param $id
     * @return array
     */
    public function processInfo($id): array
    {
        /** @var TaskExec $info */
        $info = $this->taskExecDao
            ->where(['id' => $id])
            ->with('user')
            ->with('taskArbitration')
            ->first();
        if (!$info) {
            throw new BusinessException(ErrorCode::TASK_PROCESS_NOT_EXIST);
        }

        $info->remaining_time_surplus = ($info->remaining_time - time()) > 0 ? ($info->remaining_time - time()) : 0;
        $info->audit_time_end_surplus = ($info->audit_time_end - time()) > 0 ? ($info->audit_time_end - time()) : 0;
        $info->audit_fail_surplus = ($info->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time(
            )) > 0 ? ($info->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time()) : 0;

        return [
            'info' => $info
        ];
    }


    /**
     * 任务进度-审核
     * @param $id
     * @param array $data
     */
    public function processAudit($id, array $data): void
    {
        if ($data['audit_status'] == TaskExecConstants::AUDIT_STATUS_FAIL && $data['audit_fail_context'] == '') {
            throw new BusinessException(ErrorCode::TASK_AUDIT_FAIL_CONTEXT_REQUIRED);
        }
        //加锁
        $Lock = new Lock();
        $lockKeys = [];

        $lockKeyTmp1 = $Lock->locks(
            [
                RedisKey::LOCK_MYSQL_TABLE_ID . 'task_exec:' . $id
            ],
            DefaultValue::LOCK_TTL
        );
        if (!$lockKeyTmp1) {
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }
        $lockKeys = array_merge($lockKeys, $lockKeyTmp1);

        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao
            ->where(['id' => $id])
            ->with('task')
            ->first();
        if (!$taskExec) {
            $Lock->unlocks($lockKeys);
            throw new BusinessException(ErrorCode::TASK_PROCESS_NOT_EXIST);
        }

        //不要重复审核
        if ($taskExec->audit_status != TaskExecConstants::AUDIT_STATUS_NOT) {
            $Lock->unlocks($lockKeys);
            throw new BusinessException(ErrorCode::TASK_AUDIT_REPEAT);
        }

        Db::beginTransaction();
        try {
            $taskExec->audit_status = $data['audit_status'];
            if ($data['audit_status'] == TaskExecConstants::AUDIT_STATUS_SUCCESS) {
                //审核通过，任务进度结束
                $taskExec->finish_status = TaskExecConstants::FINISH_STATUS_YES;
                $taskExec->save();

                //给用户佣金
                $this->taskLogic->commissionIncome(
                    [
                        'user_id' => $taskExec->user_id,
                        'task_id' => $taskExec->task_id
                    ]
                );

                //截图审核通过通知
                Sender::send(
                    [$taskExec->user_id],
                    new UserTaskAuditSuccess(
                        [
                            'content' => [
                                'task_title' => $taskExec->task->title
                            ],
                            'routeParams' => [
                                'task_exec_id' => $taskExec->id,
                                'task_id' => $taskExec->task->id
                            ]
                        ]
                    )
                );
            } elseif ($data['audit_status'] == TaskExecConstants::AUDIT_STATUS_FAIL) {
                $taskExec->audit_fail_context = $data['audit_fail_context'];
                $taskExec->audit_fail_number = $taskExec->audit_fail_number + 1;
                $taskExec->audit_fail_time = time();
                $taskExec->save();

                //延迟队列-48小时用户不重新提交返还任务
                $message = new TaskReturnProducer(['taskExecId' => $taskExec->id]);
                $this->delayProducer->produce($message, 86400 * 2);

                //截图审核未通过通知
                Sender::send(
                    [$taskExec->user_id],
                    new UserTaskAuditFail(
                        [
                            'content' => [
                                'audit_fail_context' => $taskExec->task->title
                            ],
                            'routeParams' => [
                                'task_exec_id' => $taskExec->id,
                                'task_id' => $taskExec->task->id
                            ]
                        ]
                    )
                );
            }

            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Log::get()->error($e->getCode(), [$e->getMessage(), $data]);
            Log::get()->error($e->getTraceAsString());
//            var_dump($e->getMessage());
            throw new BusinessException(ErrorCode::TASK_AUDIT_ERROR);
        } finally {
            $Lock->unlocks($lockKeys);
        }
    }

    /**
     * 删除步骤图
     * @param array $data 数据
     * @param string $keyPrefix key前缀
     * @param int $length 步骤长度
     * @return array
     */
    private function uploadStepImage(array $data, string $keyPrefix, int $length = 0): array
    {
        $filePathStepImage = [];
        for ($i = 0; $i < $length; $i++) {
            $stepImageKey = "{$keyPrefix}{$i}";
            $filePathStepImage[$i] = '';
            if (isset($data[$stepImageKey])) {
                $filePathStepImage[$i] = $data[$stepImageKey];
//                $filePathStepImage[$i] = $this->moveTmpFile($data[$stepImageKey], '/upload/task');
            }
        }
        return $filePathStepImage;
    }

    /**
     * 删除步骤图
     * @param array $filePath
     */
    private function deleteStepImage(array $filePath)
    {
        foreach ($filePath as $v) {
            if ($v != '') {
                $this->deleteFile($v);
            }
        }
    }


    /**
     * 自动打款
     * 注：因在amqp中有使用，不使用context
     * @param array $data
     * @return string
     */
    public function taskAutoPayment(array $data): string
    {
        $Lock = new Lock();

        $lockKeys = [];
        $lockKeyTmp1 = $Lock->locks(
            [
                RedisKey::LOCK_MYSQL_TABLE_ID . 'task_exec:' . $data['taskExecId']
            ],
            DefaultValue::LOCK_TTL
        );
        if (!$lockKeyTmp1) {
            return Result::REQUEUE;
        }
        $lockKeys = array_merge($lockKeys, $lockKeyTmp1);

        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao->first(['id' => $data['taskExecId']]);
        if (!$taskExec) {
            $Lock->unlocks($lockKeys);
            return Result::DROP;
        }

        if ($taskExec->status == TaskExecConstants::STATUS_HAS_AUDIT
            && $taskExec->audit_status == TaskExecConstants::AUDIT_STATUS_NOT
        ) {
            Db::beginTransaction();
            try {
                //审核通过，任务进度结束
                $taskExec->finish_status = TaskExecConstants::FINISH_STATUS_YES;
                $taskExec->save();

                //给用户佣金
                $this->taskLogic->commissionIncome(
                    [
                        'user_id' => $taskExec->user_id,
                        'task_id' => $taskExec->task_id
                    ]
                );

                Db::commit();
            } catch (\Throwable $e) {
                Db::rollBack();
                return Result::REQUEUE;
            } finally {
                $Lock->unlocks($lockKeys);
            }
        } else {
            $Lock->unlocks($lockKeys);
        }
        return Result::ACK;
    }

    /**
     * 悬赏主 审核倒计时快到期限提醒
     * @param array $data
     * @return string
     */
    public function taskAuditWait(array $data): string
    {
        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao
            ->where(['id' => $data['taskExecId']])
            ->with('task')
            ->first();
        if (!$taskExec) {
            return Result::DROP;
        }

        if ($taskExec->status == TaskExecConstants::STATUS_HAS_AUDIT
            && $taskExec->audit_status == TaskExecConstants::AUDIT_STATUS_NOT) {
            //判断两个小时内是否有发过且未读
            $beforeTime = time() - NoticeConstants::BEFORE_TIME_HAS_NOT_READ;
            $beforeDate = date('Y-m-d H:i:s', $beforeTime);

            //悬赏主端-审核倒计通知提醒
            $hasNotRead = $this->noticeEmployerDao
                ->where(
                    [
                        'employer_id' => $taskExec->employer_id,
                        'type' => NoticeConstants::EMPLOYER_TASK_AUDIT_WAIT,
                        'status' => NoticeConstants::STATUS_UNREAD,
                        'created_at_gt' => $beforeDate
                    ]
                )
                ->count();

            if ($hasNotRead == 0) {
                Sender::send(
                    [$taskExec->employer_id],
                    new EmployerTaskAuditWait(
                        [
                            'routeParams' => [
                                'id' => $taskExec->id,
                                'task_id' => $taskExec->task->id
                            ]
                        ]
                    )
                );
            }

            //合作商端-审核催促
            $cooperativeUserIds = $this->cooperativeUserDao->getIds($taskExec->cooperative_id);
            $hasNotice = $this->noticeCooperativeDao
                ->where(
                    [
                        'type' => NoticeConstants::COOPERATIVE_TASK_AUDIT_WAIT,
                        'status' => NoticeConstants::STATUS_UNREAD,
                        'created_at_gt' => $beforeDate,
                        'cooperative_user_ids' => $cooperativeUserIds
                    ]
                )
                ->get();


            $hasNoticeIds = [];
            if ($hasNotice->count() > 0) {
                $hasNoticeArray = $hasNotice->toArray();
                $hasNoticeIds = array_column($hasNoticeArray, 'cooperative_user_id');
            }
            $needSendIds = array_diff($cooperativeUserIds, $hasNoticeIds);
            if (count($needSendIds) > 0) {
                Sender::send(
                    $needSendIds,
                    new CooperativeTaskAuditWait()
                );
            }
        }

        return Result::ACK;
    }


    public function noticeTaskAuditPending(array $data): string
    {
        //判断两个小时内是否有发过且未读
        $beforeTime = time() - NoticeConstants::BEFORE_TIME_HAS_NOT_READ;
        $beforeDate = date('Y-m-d H:i:s', $beforeTime);

        if ($data['noticeSendPlatform'] == NoticeConstants::SEND_PLATFORM_COOPERATIVE) {
            $taskAuditCount = $this->taskDao
                ->where(
                    [
                        'cooperative_id' => $data['cooperative_id'],
                        'is_audit_by' => TaskConstants::IS_AUDIT_BY_COOPERATIVE,
                        'audit_status' => TaskConstants::AUDIT_STATUS_PENDING
                    ]
                )
                ->count();

            if ($taskAuditCount >= NoticeConstants::TASK_AUDIT_MAX) {
                $cooperativeUserIds = $this->cooperativeUserDao->getIds($data['cooperative_id']);

                $hasNotice = $this->noticeCooperativeDao
                    ->where(
                        [
                            'type' => NoticeConstants::COOPERATIVE_TASK_AUDIT_PENDING,
                            'status' => NoticeConstants::STATUS_UNREAD,
                            'created_at_gt' => $beforeDate,
                            'cooperative_user_ids' => $cooperativeUserIds
                        ]
                    )
                    ->get();

                $hasNoticeIds = [];
                if ($hasNotice->count() > 0) {
                    $hasNoticeArray = $hasNotice->toArray();
                    $hasNoticeIds = array_column($hasNoticeArray, 'cooperative_user_id');
                }

                $needSendIds = array_diff($cooperativeUserIds, $hasNoticeIds);
                if (count($needSendIds) > 0) {
                    Sender::send(
                        $needSendIds,
                        new CooperativeTaskAuditPending()
                    );
                }
            }
        } elseif ($data['noticeSendPlatform'] == NoticeConstants::SEND_PLATFORM_ADMIN) {
            $taskAuditCount = $this->taskDao
                ->where(
                    [
                        'cooperative_id' => $data['cooperative_id'],
                        'is_audit_by' => TaskConstants::IS_AUDIT_BY_ADMIN,
                        'audit_status' => TaskConstants::AUDIT_STATUS_PENDING
                    ]
                )
                ->count();

            if ($taskAuditCount >= NoticeConstants::TASK_AUDIT_MAX) {
                $adminUserIds = $this->adminUserDao->getIds();

                $hasNotice = $this->noticeAdminDao
                    ->where(
                        [
                            'type' => NoticeConstants::ADMIN_TASK_AUDIT_PENDING,
                            'status' => NoticeConstants::STATUS_UNREAD,
                            'created_at_gt' => $beforeDate,
                            'admin_user_ids' => $adminUserIds
                        ]
                    )
                    ->get();

                $hasNoticeIds = [];
                if ($hasNotice->count() > 0) {
                    $hasNoticeArray = $hasNotice->toArray();
                    $hasNoticeIds = array_column($hasNoticeArray, 'admin_user_id');
                }

                $needSendIds = array_diff($adminUserIds, $hasNoticeIds);
                if (count($needSendIds) > 0) {
                    Sender::send(
                        $needSendIds,
                        new AdminTaskAuditPending()
                    );
                }
            }
        }

        return Result::ACK;
    }

}